翻轉電子書系列:Java 程式設計()含物件導向 描述: 回首頁.png 

翻轉工作室:粘添壽

 

第七章  類別與物件產生

7-1 物件導向設計理念

7-1-1 軟體 IC – 物件

利用物件變數來描述真實環境,可能會超出原來環境的事實而不知曉。在許多情況下,所描述的真實現象需要有相當的限制,才不會超出原來的意義。譬如,真實環境會有下列限制:

當我們利用某些變數去描述上述現象時,希望能具有上述限制的功能。也就是說,描述真實環境的變數,本身需要有判斷處理的功能,當所描述的環境因素超過了真實現象,必須主動拒絕。

在許多情況下,僅拒絕不合理的變數產生,並不一定能滿足現實環境的需求,可能需要變數能自我調整,並使其合理化才是正當的做法。由上述的需求可以看出,變數不再僅是某一記憶體空間的內容,也不僅被動的填入某一數值內容就能滿足,它必須具有適當的處理能力。

如此一來,描述環境的變數則成為處理單元(Process),它是一個堅固的個體,裡面包含著適當的記憶體(變數)與程式(方法),並且針對某一種特殊功能而製作,我們就將它稱為『物件』(Object)。利用物件所描述真實環境,與編寫程式的技巧,則稱為『物件導向』(Object-oriented)。

當物件被產生之後,就擁有自己的獨立運作記憶體空間,與產生物件的主程式記憶體空間並不相衝突。由前文可知,物件是能提供某一種特殊功能的獨立個體,類似組合電子電路的『積體電路晶片』(IC)。我們也可以取得現有的物件個體,組成一套系統功能較強的軟體套件,因此,吾人將物件個體稱之為『軟體 IC』。硬體方面的 IC 晶片可被組合一個較大、功能較強的電路板,當需要更複雜設備時,又可以組合多個電路板,成為一套功能更強的資訊設備。同樣的,軟體 IC 的物件個體也同樣具有這些功能;我們組合多個物件個體,成為一套功能較強的軟體套件,但基本上他還是具有物件個體的特質。我們還是可以組合多個物件導向的軟體套件,成為另一套功能更強的軟體系統。更重要一點,這些軟體 IC 並不需要自行開發,可多人共同開發,或經由購買專屬套件取得。簡單的說,利用物件導向技術所開發出來的物件個體,它可以像 IC 晶片般的流通。當然啦!物件個體還有繼承性與多形性特質,我們於第九章再來討論。

7-2-1 物件的成員

簡單的說,物件即是活動式(Activity)描述真實環境的個體,為了使它具有主動式功能,物件個體內必須具有相當的處理能力。圖 7-1 為物件個體(Object body)內容的抽象圖,包含有:

7-1 物件個體的內容

7-1-3 變數成員的屬性

變數成員(Variable member)是提供物件所能描述真實環境的參數,可以由基本資料型態(如 intfloatchararray...等)、或其他物件所組成。依照變數可被存取的屬性區分為:

7-2 表示某一類別或物件執行當中可能出現的現象,假設某一類別導入(import)另一個類別,並由新類別產生另一個物件(object_1)。新的物件裡有 3 個變數成員,其中 v1 v3 成員為隱藏性變數(private);外部程式無法直接存取,必須呼叫該物件的方法成員(method_1() method()),透過它們存取 v1 v3 內容。另外,v2 是屬於公開性變數(非隱藏性),物件外程式可以直存取其內容。

7-2 變數成員的屬性

雖然物件所提供的特殊功能,大多是利用方法成員來實現。但隱藏性變數一定需透過某些方法成員,才可以外界溝通,因此稱提供此功能的方法為『物件輸出/輸入埠口』(如圖 7-1 所示)。物件方法提供填入物件變數內容功能者,稱之為『輸入埠口』;反之,提供攫取變數內容的方法,則稱之為『輸出埠口』。

7-1-4 方法成員的屬性

方法成員(Method member) 是物件個體內的函數,可執行某一特定功能的程式。最基本的功能是提供變數成員的輸入與輸出介面,亦可區分為:

7-3 方法成員的屬性

吾人以圖 7-3 來說明方法成員的屬性如何。假設某一執行中的類別經由某一類別產生了兩個物件(object_1 object_2),外部方法(external method)可以直接呼叫執行物件內的公開性方法(public method),但無法呼叫私有性與保護性方法。然而私有性法(private method)可以被相同物件內的其他方法呼叫;保護性方法(protected method 可以被由相同類別產生物件內的方法呼叫。

7-1-5 類別與物件的關聯

簡單的說,類別(Class)就是物件的藍圖,或稱為描述檔。我們利用類別描述所期望建立物件的內容,需要時再利用類別產生物件(new 的功能)。就好像蓋房子一樣,類別即是設計房子的藍圖,利用此藍圖經由實地建造之後,便完成所設計的房子,該房子即是物件的意思。一份建築藍圖可以多次使用,蓋出許多相同樣子的房子;也就是說,一只類別可以產生多個名稱不同但功能相同的物件(如圖 7-4 所示)。

一份建築藍圖可經由增減功能成另一份建築藍圖,再利用新的建築藍圖去蓋另一種房屋;同樣的道理,一份類別可經由增加或修改功能成另一個新的類別,再利用新類別去產生另一種不同型態的物件,這就是類別的繼承性。

但建築圖與類別之間還是稍有不同,建築圖必須經過施工建設才能完成一間房屋才能居住;類別則不然,它不一定需要經過建設產生(new() 操作)才能使用,許多地方可以導入類別並直接執行,這方面本書預留在第九章介紹。本章僅介紹需經由類別產生物件後才可以執行。

7-4 類別與物件的關聯

7-2 類別的產生

7-2-1 類別宣告語法 - class

本書於第八章有稍稍介紹類別宣告的語法,但並不完整,這裡再重新詳細介紹,語法與範例如下:

類別及成員宣告語法:

範例:

[modifier] class class_name {

[modifier] declaration variable 1;

[modifier] declaration variable 2;

…..

[modifier] declaration method 1;

[modifier] declaration method 2;

….

}

public class Employee {

private int ID;

String name;

…….

int getID() { …..}

void setID(int i) { ….}

……

}

宣告類別包含類別本身(class body)、類別內的變數成員(class variable)、以及方法成員(class method)等三種元素,這三種元素都可分別宣告其屬性。

屬性有共通的表現方法,即是宣告語法前面有一個修飾字(modifier)選項(中括號 [ ] 表示選項的意思),表示該類別(或類別成員、類別方法)可以被存取的屬性宣告。類別與方法成員的修飾字有 public private等兩種,變數成員有 publicprivate protected 三種屬性格式,說明如下:

7-2-2 方法成員的宣告

如果類別內的方法是針對類別變數處理的話,則該類別所衍生的物件就成為一個很堅固的結構資料;如果是針對某一種特殊功能所設計方法的話,則該類別所衍生出來的物件,便成為專屬功能的函數了。由此可見,方法成員即是實現物件功能的主要藍圖。方法成員的宣告語法如下:

宣告方法成員語法:

範例:

[modifier] retrun_type method_name ([arg]{

….

method_body

…..

}

int setPay(int pay) {

if (pay <158000) {

    System.out.printf(“底薪太低\n”);

    retrun 0;

}

else

     payment = pay; return 1;

}

重點說明如下:

7-2-3 變數成員的宣告

在物件導向程式裡,變數成員代表著許多重大的意義,可能是描述某一真實物件的屬性,這些屬性都代表著某一特別的參數。簡單的說,類別變數的集合就好像 C 語言的結構變數一樣的功能。宣告語法與範例如下:

類別及成員宣告語法:

範例:

data_type variable_name;

[modifier] data_type variable_name;

String name;

private int ID;

重點說明如下:

7-2-4 主方法的宣告 – main()

一個完整的程式,可能由多個類別所建構而成,然而每一個類別裡也許又包含了若干個方法,當這個程式被啟動時,到底是由哪一個類別中的哪一個方法開始執行?Java 程式與 C 程式語言都是由 main() 程序(Java 稱為方法),啟動開始執行。但 Java 程式有較特殊的地方,因為它是由多個類別所構成,必須指名是哪一個類別底下的方法,因此類別名稱必須與檔案名稱相同;範例如下:

//Hello.java

public class Hello {

    public static void main(String[] args) {

          System.out.println("Hello !! Your welcome");

          System.out.println(“Java 程式設計 !! 歡迎您 “);

    }

}

重點說明如下:

7-3 物件的產生

7-3-1 物件產生敘述 – new()

設計好類別藍圖之後,接下來必須考慮如何將類別實現成為一個可執行運作的『物件』。像是蓋房子一樣,同一張建築藍圖可能因所需的人不同、所蓋的地理位不同、所建設的時間不同等等不同因素,即使照圖蓋出外觀一樣的房子,內裝樣式卻可能出現很大的差別。由此可見,由同一份類別所產生的物件之間,也會因所面臨的狀況不同,產生外觀相同,內部大異其趣的物件。因此,任一時機內,由類別產生一個物件,只不過當時狀況的一個『驗例』(Instance)而已,由類別產生物件的過程稱為『驗例化』(Instantiate,某些地方稱為物件化)。利用 new () 函數呼叫,由類別產生一個物件,語法如下:

 

物件產生語法

      

物件宣告

class_name object_name;

Employee Liu;

物件產生

object_name = new class_name();

Liu = new Employee();

物件宣告並

產生

class_name object1 = new class_name();

Employee Cheng = new Employee()

物件內變數

object_name.variable_name

Cheng.name

物件內方法

object_name.method_name

Cheng.setID()

重點說明如下:

上述範例中,Employee 為事先製作好的類別,並經過編譯成 Employee.classBytecode,中介程式);而且須與原程式儲存放於同一目錄底下,否則必須利用 classpath 變數(第一章有設定 classpath 說明),標明 Employee.class 的所在位置。利用 new 產生的物件其特性如下:

object_name.variable_name』(如 Cheng.name

object_name.method_name』(如 Cheng.setID()

7-3-2 範例研討:規劃通用型人事資料

A)程式功能:Ex7_1.javaEmployee.java

展鵬網路行銷公司需要一套較完整的人事資訊系統,該系統允許編輯員工資料,每一員工的描述屬性有:

請先建立一只雛型程式,允許輸入員工資料,如輸入資料不符規定,則不予輸入並顯示錯誤原因。輸入完畢後印出該員工的薪資表,其中加班費計算方式為每小時 = (底薪 / (30 * 8)) * 1.5。期望操作介面如下:

D:\Java2_book\chap7\Ex7_1>javac Employee.java   【編譯 Employee 類別】

D:\Java2_book\chap7\Ex7_1>javac Ex7_1.java      【編譯 Ex7_1 類別】

 

D:\Java2_book\chap7\Ex7_1>dir/b        Employee.class 須於同一目錄】

Employee.class

Employee.java

Ex7_1.class

Ex7_1.java

 

D:\Java2_book\chap7\Ex7_1>java Ex7_1

** 展鵬資訊 建立員工資料 **

請輸入 員工姓名 =>張大得

請輸入員工部門 = >資訊部

請輸入員工代號 =>1201

請輸入員工底薪 =>45000

請輸入員工加班時數 =>23

**** 列印員工薪資表 *****

代號   姓名     部門    本月薪資

1201    張大得  資訊部  49301

D:\Java2_book\chap7\Ex7_1>java Ex7_1

** 展鵬資訊 建立員工資料 **

請輸入 員工姓名 =>張第一

請輸入員工部門 = >管理部

請輸入員工代號 =>134

1000 <員工代號 < 5000 範圍 !!

程式停止運作

D:\Java2_book\chap7\Ex7_1>java Ex7_1

** 展鵬資訊 建立員工資料 **

請輸入 員工姓名 =>林添財

請輸入員工部門 = >管理部

請輸入員工代號 =>1203

請輸入員工底薪 =>12000

底薪不可低於 15800

程式停止運作

D:\Java2_book\chap7\Ex7_1>java Ex7_1

** 展鵬資訊 建立員工資料 **

請輸入 員工姓名 =>林森林

請輸入員工部門 = >製造部

請輸入員工代號 =>1208

請輸入員工底薪 =>24000

請輸入員工加班時數 =>56

勞基法規定加班時數不可超過 45 小時

程式停止運轉

B)製作技巧研討:

物件的簡單應用,如同傳統語言的『結構變數』一樣,大多運用於描述環境的事實現象(或稱 Entity)。但物件不僅被動的描述 Entity 的屬性外,如果給予適當的處理,它可主動判斷『事項』是否已超過真實現象。以電子化人事系統為例,對於公司內員工薪資或工時計算大多不可以違反『勞工法』規定,譬如員工代號大多有一定格式、勞工最低薪資、每月最高超工時數、等等。

我們相信公司裡某些規定是適合所有員工,也就是員工資料制定完成之後,大多可以適用所有部門員工。因此,吾人依照展鵬公司的需求,製作了如圖 7-5 Employee 類別(Employee.java),其他應用系統也可直接引用,如此則可以整合公司內的員工資料。圖 7-5 是由 Employee 類別產生 worker sales 員工物件的範例。設計完成員工資料類別之後,再編寫建立員工資料程式(Ex7_1.java)就不會困難了。

7-5 員工資料的物件範例

C Employee.java 程式範例:

吾人利用 Employee.java 建構宣告員工物件的類別(Employee.class);該類別內包含 5 個變數成員(其中 3 個是隱藏式變數),與 6 個存取隱藏變數的方法成員,然而它們即是該類別的輸入/輸出埠口。從外觀來看,Employee.class 抽象功能如圖 7-6 所示。各項功能如下說明:

7-6 Employee.class 類別架構

7-7 Employee.java 程式架構

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

//Employee.java

 

class Employee {

 

    /* 宣告物件成員 ***/

    private int ID;            // 員工代號, 介於 1000~5000

    String name;               // 員工姓名

    String depart;             // 所屬部門

    private int payment;       //     > 15800

    private int extra;         // 加班時數 < 45

 

    /*** 宣告物件方法 ***/

    int setID(int i) {      // 設定員工代號方法, 1: 正常, 0: 錯誤

         if((i>1000) && (i <5000)) {

              ID = i;

              return 1;

         }

         else {

              System.out.printf(" 1000 <員工代號 < 5000 範圍 !!\n");

              return 0;

        }

    }

    int getID() {            // 讀取員工代號方法

         return ID;

    }

    int setPayment(int pay) {   //設定底薪方法, 1: 正常, 0: 錯誤

         if (pay < 15800) {

             System.out.printf("底薪不可低於 15800 \n");

             return 0;

         }

         else {

             payment = pay;

             return 1;

         }

     }

     int getPayment() {      // 讀取底薪方法

         return payment;

     }

     int setExtra(int ex) {    // 設定加班時數方法, 1: 正常, 0: 錯誤

         if (ex > 45) {

             System.out.printf("加班時數不可超過 45 小時\n");

             return 0;

         }

         else {

             extra = ex;

             return 1;

         }

     }

     int getExtra() {        // 取得加班時數方法

         return extra;

     }

}

程式重點說明:

DEx7_1.java 程式範例

7-8 Ex7_1.java 程式架構

製作完成 Employee 類別之後,吾人再編寫一主程式,引用該類別來產生工作員(worker)。針對 worker 物件輸入與輸出資料,證實 Employee 類別所提供的方法與變數成員是否能滿足所需。

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

//Ex7_1.java

/* 請建立一套人事資訊系統,該系統允許員工輸入資料,並印出該員工的薪資表 */

 

import java.util.*;

// Employee.class 於同一目錄下

public class Ex7_1 {

    public static void main(String args[]) {

         Scanner keyin = new Scanner(System.in);

         Employee worker = new Employee();

         int id, pay, ex, pay1;

        

         System.out.printf("** 展鵬資訊 建立員工資料 **\n");

         System.out.printf("請輸入 員工姓名 =>");

         worker.name = keyin.nextLine();

         System.out.printf("請輸入員工部門 = >");

         worker.depart = keyin.nextLine();

 

         System.out.printf("請輸入員工代號 =>");

         id = keyin.nextInt();

         keyin.nextLine();

         if (worker.setID(id)==0) {

              System.out.printf("程式停止運作 \n");

              return;

         }

 

         System.out.printf("請輸入員工底薪 =>");

         pay = keyin.nextInt();

         keyin.nextLine();

         if (worker.setPayment(pay) == 0) {      // 判斷是否正常設定

              System.out.printf("程式停止運作 \n");

              return;

         }

 

         System.out.printf("請輸入員工加班時數 =>"); 

         ex = keyin.nextInt();

         keyin.nextLine();

         if (worker.setExtra(ex) == 0) {            // 判斷是否正常設定

               System.out.printf("程式停止運轉 \n");

               return;

         }

 

         System.out.printf("**** 列印員工薪資表 *****\n");

         System.out.printf("代號   姓名     部門    本月薪資\n");

         System.out.printf("%d\t", worker.getID());

         System.out.printf("%s\t", worker.name);

         System.out.printf("%s\t", worker.depart);

         pay = worker.getPayment();

         pay1 = (int)((double)pay/(30 * 8)) * worker.getExtra();

         System.out.printf("%d\n", pay+pay1);

    }

}

程式重點說明:

E)直接存取隱藏性變數的結果

無人稍修改 Ex7_1.java 程式,測試直接存取 worker.IDworker.extra worker.payment 等隱藏性變數。修改部分程式內容:Ex7_1_1.java

……..

/* 驗證直接存取 private ID 的結果 */

         id = worker.ID;            // 直接存取 private 屬性的變數成員

         System.out.printf("ID = %d\n", id);

/* 結束驗證 */                    

……..

其編譯結果如下:

D:\Java2_book\chap7\Ex7_1>javac Ex7_1_1.java

Ex7_1_1.java:27: error: ID has private access in Employee

         id = worker.ID;

                    ^

1 error

 

 

7-3-3 自我挑戰:驗證身分證字號

A)程式功能:PM7_1.javaCustomer.java

『真水水塑身美容中心』希望建立一套客戶資料管理系統,登錄客戶資料,針對每位客戶包含有下列資料:

請建立一個雛型客戶資料管理系統,測試系統規劃是否完備;該系統允許輸入客戶資料,再顯示是否正確。期望操作介面如下。

D:\Java2_book\chap7\PM7_1>javac Customer.java    【編譯 Customer.java

D:\Java2_book\chap7\PM7_1>javac PM7_1.java      【編譯 PM7_1.java

D:\Java2_book\chap7\PM7_1>dir/b    

Customer.class

Customer.java

PM7_1.class

PM7_1.java

D:\Java2_book\chap7\PM7_1>java PM7_1

** 真水水朔身中心 建立客戶資料 **

請輸入 客戶姓名 =>張美麗

請輸入身分字號 = >A277465487

請輸入性別 =>

請輸入年齡 =>30

請輸入地址 =>高雄市鳥松區

=== 列印客戶資料 ===

身分證號        姓名  性別  年齡    地址

A277465487      張美麗        30      高雄市鳥松區

D:\Java2_book\chap7\PM7_1>java PM7_1

** 真水水朔身中心 建立客戶資料 **

請輸入 客戶姓名 =>劉美麗

請輸入身分字號 = >K123456723

這是偽造的 !!

程式停止運作

D:\Java2_book\chap7\PM7_1>java PM7_1

** 真水水朔身中心 建立客戶資料 **

請輸入 客戶姓名 =>劉大勇

請輸入身分字號 = >A277465487

請輸入性別 =>

僅服務女性 !! 對不起

程式停止運作

B)製作技巧提示

本系統最大的特點是,必須偵測所輸入的身分證字號是否正確,由這一點可觀察出來客戶所提供的資料是否偽造的。因此,需在客戶資料類別(Customer.class)必須提供此檢查功能。身份證號碼第一個字元 A ~ Z表示地區,第二個字元 1(男性)或 2(女性)表示性別,後面緊接著 7 個數字(0 ~ 9),最後是檢查碼(0~9),總共 10 個字。

檢查方法如下:首先將英文字母以(A=10 B=11 C=12 D=13 E=14 F=15 G=16 H=17 J=18 K=19 L=20 M=21 N=22 P=23 Q=24 R=25 S=26 T=27 U=28 V=29 W=30 X=31 Y=32 Z=33 I=34 O=35)轉換成數字,與原有數字組成 11 個數字;接著,第 1 數乘以 1、第 2 數乘以 9、第 3 個數乘以 8 ,依此類推第 9 個數乘以 2,第 10 個數乘以 1,將上述的結果相加,總和的個位數(num),再被 10 減的結果(7-mun),與第 11 個數字相同,則表示正確。

本系統需要 Customer.java PM7_1.java 兩只類別程式,前者規劃 Customer 類別使用;後者為主類別程式,本書僅提示 Customer.java 程式,另者請讀者自行編寫(請參考範例 Ex7_1.java)。

CCustomer.java 程式範例

吾人針對系統要求登錄客戶哪些資料與相關限制,製作了 Customer 類別(Customer.java),其功能架構如圖 7-7 所示,各項功能如下:

7-9 Customer 類別功能架構

7-10 Customer.java 程式架構

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

//Customer.java

 

import java.util.Scanner;

class Customer {

    private String SID;        // 身分證字號, 須符合格式

    String name;               // 客戶姓名

    private String sex;        //     , 僅提供女性

    int age;                   //    

    String addr;               //    

 

    int setSID(String No) {   // 設定身分字號方法, 1: 正常, 0: 錯誤

         String[] dig1 = new String[10];   // 儲存身分證號字元

         int[] dig2 = new int[11];        // 儲存轉換後數字

         int temp;

 

         if (No.length() != 10) {

              System.out.printf(“ 10 個字元, 請重新輸入 !!”);

              return 0;

         }

         Scanner s = new Scanner(No).useDelimiter(“”);

         for(int I=0; I<10; I++)

             dig1[I] = s.next();

 

         temp = Character.getNumericValue(dig1[0].charAt(0));

         if (temp == 18)    // 字母 I, 設定為 34

              temp = 34;

         else if (temp == 24)  // 字母 O, 設定為 35

              temp =35;      

         else if((temp>18) || (temp < 24))  // J ~ N 前進 1 個數

              temp = temp – 1;

         else if (temp > 24)

              temp = temp – 2;         // P ~ Z 前進 2 個數

 

         for(int I=1; I<10; I++)

              dig2[I+1] = Integer.parseInt(dig1[I]);

         dig2[0] = temp / 10;

         dig2[1] = temp %10;

        

         int sum = dig2[0];       // 1 個數字乘以 1

         int k = 9;

         for(int I=1; I<10; I++) {       // 2 ~ 9 數字分別

              sum = sum + dig2[I] * k;   // 乘以 9 ~ 1 累加

              k = k-1;

         }

 

         int check = 10 –(sum % 10);    // 10 – 個位數

         if (check == dig2[10]) {      // 是否與第 11 數字相同

               SID = No;

               return 1;

         }

         else {

              System.out.printf(“這是偽造的 !! \n”);

              return 0;

         }

     }

            

    String getSID() {            // 讀取員工代號方法

         return SID;

    }

    int setSex(String Sex) {   //設定性別方法, 1: 正常, 0: 錯誤

         if (Sex.equals(“”)) {

              sex = Sex;

              return 1;

         }

         else {

              System.out.printf(“僅服務女性 !! 對不起 \n”);

              return 0;

         }

    }

    String getSex() {     // 取的客戶性別方法

         return sex;

    }

}

程式重點說明:

CPM7_1.java 程式提示

請讀者自行編寫,程式架構如圖 7-9 所示。

7-11 PM7_1 程式架構

7-4 靜態變數的應用

7-4-1 類別變數 - static

基本上,經由類別產生若干個物件後,各個物件都屬獨立性個體,有自己的記憶體空間、變數成員與方法成員。但某些情況下,我們還是希望同一類別所產生的物件們,之間保留有一些連帶關係。讓物件之間保留關係,最簡單的方法的讓物件之間享有共同的變數成員,此型態變數稱為『靜態變數』(Static variable)或『類別變數』(Class variable)。類別實體內宣告某一個靜態變數(如 static int a;)之後,無論該類別產生多個物件(如 xyz...等),靜態變數都指向同一變數(如 x.ay.az.a);也就是說,改變任一物件的靜態變數(如 x.a)的內容,則其他靜態變數的內容也隨之改變(如 y.a z.a);如圖 7-8 所示。

7-12 靜態變數的特性

靜態變數的宣告語法如下:

class product {

   …..

   static int total;

}

重點說明如下:

7-4-2 範例研討:智慧型庫存管理系統

A)程式功能:Ex7_2.java

許多經理人都想盡辦法降低庫存量與增加銷售量,來增加公司的獲利,但兩者其實互相衝突。當庫存量不足時,可能會嚴重影響到銷售的順暢性,庫存量太高則會積壓公司資金,如何掌握最恰當的庫存量,實為考驗經理人能力的關鍵,最簡單的方法是隨時掌握目前庫存金額多寡。因此,『春嬌超商連鎖公司』期望建立一套智慧型的庫存管理系統。

該系統允許選擇商品及進貨或出貨數量,進貨用正號(如 +10);出貨用減號(如 -10)。庫存檔案儲存於 product.data 內,每一商品包含四個欄位:{品名、單價、庫存量、金額、總庫存金額}(如 可口奶滋 ),其中 "金額" 為該項產品庫存金額。"總庫存金額" 為所有庫存量的總金額;進出任何商品後,總庫存金額會登錄目前所有金額多寡。除了能滿足一般庫存系統的功能外,也能讓經理人隨時了解目前庫存金額多寡。期望操作介面如下:

D:\Java2_book\chap7\Ex7_2>javac Ex7_2.java

 

D:\Java2_book\chap7\Ex7_2>dir/b

Ex7_2.class                               【主程式類別】

Ex7_2.java                               【主程式檔案】

Product.class                              【產品類別】

Product.data                              【產品資料檔案】

 

D:\Java2_book\chap7\Ex7_2>java Ex7_2

**** 未登錄前庫存量 ***

品名            單價    庫存量  金額    總庫存金額

可口奶滋        20      50      1000    2780

黑松汽水        12      40      480     2780

味全鮮乳        15      50      750     2780

頻果西打        8       80      640     2780

 

(1) 可口奶滋    (2) 黑松汽水    (3) 味全鮮乳    (4) 頻果西打

        請輸入貨品編號(或大於 5 離開) =>4

         進出貨數量 =>20

品名            單價    庫存量  金額    總庫存金額

可口奶滋        20      50      1000    2940

黑松汽水        12      40      480     2940

味全鮮乳        15      50      750     2940

頻果西打        8       100     800     2940

總庫存金額(product.total) = 2940

B)製作技巧研討:

吾人將 Product 類別中庫存總金額宣告成類別變數(static int total;),當任何一樣產品變更此內容時,所有產品的 total 值隨之改變。因此,查閱任何一筆資料都可以知道目前總庫存金額多寡。

C)程式範例:

7-13 Ex7_2 程式架構

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

//Ex7_2.java

 

import java.io.*;

import java.util.Scanner;

class Product {

     String name;          //產品名稱

     int price;            // 單價

     int storage;          // 庫存量

     int deposit;           // 金額

     static int total;     // 庫存總金額(類別變數)

}

 

public class Ex7_2 {

    public static void main(String args[]) throws IOException {

       Product[] article = new Product[20];

       Scanner keyin=new Scanner(System.in);

       String file = "Product.data";

       String inData;

       int k=0, select, count;

       File fileID = new File(file);

       if (fileID.exists()) {

           BufferedReader data = new BufferedReader(new

                                       FileReader(fileID));

           while ((inData=data.readLine()) != null) {

               Scanner s = new Scanner(inData).useDelimiter("\t");

               article[k] = new Product();

               article[k].name = s.next();

               article[k].price = s.nextInt();

               article[k].storage = s.nextInt();

               article[k].deposit = s.nextInt();

               article[k].total = s.nextInt();

               k = k +1;

           }

           data.close();

      }

      else{

           System.out.printf("%s file not existed\n", file);

           return;

      }

      System.out.printf("**** 未登錄前庫存量 ***\n");

      System.out.printf("品名\t\t單價\t庫存量\t金額\t總庫存金額\n");

      for (int i=0; i<k; i++) {

          System.out.printf("%s\t%d\t%d\t%d\t%d\n", article[i].name,

article[i].price, article[i].storage,

article[i].deposit, article[i].total);

      }

      System.out.printf("\n");

      for (int i=0; i<k; i++)   

          System.out.printf("(%d) %s\t", i+1, article[i].name);

      System.out.printf("\n");

      System.out.printf("\t請輸入貨品編號(或大於 5 離開) =>");

      select = keyin.nextInt();

      if (select < 5) {

          System.out.printf("\t 進出貨數量 =>");

          count = keyin.nextInt();

          int s1 = select-1;

          int m1 = article[s1].price * count;

          article[s1].storage = article[s1].storage + count;

          article[s1].deposit = article[s1].deposit + m1;

          article[s1].total = article[s1].total + m1;

      }

      System.out.printf("品名\t\t單價\t庫存量\t金額\t總庫存金額\n");                

      for (int i=0; i<k; i++) {

          System.out.printf("%s\t%d\t%d\t%d\t%d\n", article[i].name,

                              article[i].price, article[i].storage,

                              article[i].deposit, article[i].total);

      }

      System.out.printf("總庫存金額(product.total) = %d\n", Product.total);

  }

}

D)程式重點分析

7-4-3 範例研討:停車場管理系統

A)程式功能:Ex7_3.java

 高雄市政府公告『前金立體停車場管理系統』招標事項,該系統需具有下列功能:(假設範例)

參與投標者須建立一套模擬系統,除了可提供上述功能之外,為增加現場臨場感,需增加一項設定目前時間的功能,作為計算停車費的依據。期望雛型模擬系統具有下列操介面:

D:\Java2_book\chap7\PM7_1>javac Customer.java    【編譯 Customer.java

D:\Java2_book\chap7\PM7_1>javac PM7_1.java      【編譯 PM7_1.java

D:\Java2_book\chap7\Ex7_3>javac carTicket.java     【編譯 carTicket.java

D:\Java2_book\chap7\Ex7_3>javac Ex7_3.java        【編譯 Ex7_3.java

 

D:\Java2_book\chap7\Ex7_3>dir/b                【產生相關檔案 4 個類別】

Cars.class                                    【停車場車輛物件】

carTicket.class                                【停車票物件】

carTicket.java

Clock.class                                   【公用計時物件】

Ex7_3.class                                   【主類別】

Ex7_3.java

D:\Java2_book\chap7\Ex7_3>java Ex7_3

** 高雄市前金立體停車場 管理系統 目前時間 0 0 **

(1) 顯示目前停車      (2) 設定目前時間

(3) 車輛進入取票      (4) 車輛出場繳費

(5) 離開系統

目前車輛 0 請輸入工作選項 =>

目前車輛 0 請輸入工作選項 =>2

請輸入目前時間(/) =>08/20

 

** 高雄市前金立體停車場 管理系統 目前時間 8 20 **

(1) 顯示目前停車      (2) 設定目前時間

(3) 車輛進入取票      (4) 車輛出場繳費

(5) 離開系統

目前車輛 0 請輸入工作選項 =>3

請按<Enter>鍵取票 (票號:1) =>

目前車輛 3 請輸入工作選項 =>1

停車票號                進入時間

1               8 20

2               9 10

3               9 40

 

** 高雄市前金立體停車場 管理系統 目前時間 9 40 **

(1) 顯示目前停車      (2) 設定目前時間

(3) 車輛進入取票      (4) 車輛出場繳費

(5) 離開系統

目前車輛 3 請輸入工作選項 =>

目前車輛 3 請輸入工作選項 =>4

請出示停車票 =>2

請繳交 20 停車費(<Enter>) =>

 

** 高雄市前金立體停車場 管理系統 目前時間 9 40 **

(1) 顯示目前停車      (2) 設定目前時間

(3) 車輛進入取票      (4) 車輛出場繳費

(5) 離開系統

目前車輛 2 請輸入工作選項 =>

B)製作技巧提示:

製作此系統時需考慮車輛進出口並不一定會相同,即是系統可能由:入口(車輛進入取票功能)、出口(車輛出場繳費功能)、與管理處(顯示目前停車功能)等 3 個地方同時操作。因此需將儲存停車資料檔案設定成共享資料,為了方便系統設計,我們採用物件導向設計理念,圖 7-9 為資料流程概念圖。首先我們宣告停車票類別(carTicket),描述每張停車票的內容:ticket enterTime[2],前者為停車票號碼、後者為車輛進場時間(時與分)。另外,製作一個停車票紀錄類別 Cars,期望它是共用屬性,因此宣告成靜態類別,所有程式都可直接存取。Cars 類別包含:car[] number,前者紀錄每張停車票的資料,後者紀錄目前停車場的車輛數目。本系統也須設定一個靜態時間類別 Clock,紀錄目前時間。

7-14 範例 Ex7_3 資料流程

C)相關類別設計(carTicket.java

依照上述的規劃,我們需要 3 個類別,將其製作於 carTicket.java 檔案,編譯後會分別產生下列 3 個類別檔案(如圖 7-9 所示),功能如下:

7-10 carTicket.java 程式架構

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

//carTicket.java

 

/* 停車票  */

class carTicket {

    private static int count=0;

    private int ticket;       // 停車票號碼

    int[] enterTime = new int[2];      // 進入停車場時間

   

    int setTicket() {                 // 設定停車票號碼

        count++;

        ticket = count;

        return ticket;

    }

    int getTicket() {                 // 取得停車票號碼

        return ticket;

    }

}

/* 目前停放車輛 */

class Cars {

    static carTicket[] car = new carTicket[100];

    static int number;

}

/*公眾計時器 (利用設定輸入) */

class Clock {

     static int hour;

     static int minute;

}

 

D)主程式範例(Ex 7_3.java

7-15 Ex7_3 程式架構

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

//Ex7_3.java

/* 請建立一套停車場管理系統,功能有:顯示目前停車車輛、

   車輛進入取票、車輛出場繳費 */

 

import java.io.*;

import java.util.*;

public class Ex7_3 {

    static Scanner keyin = new Scanner(System.in);

    public static void main(String args[]) {

         Cars.number = 0;

         int select = get_menu();

         while (select != 5) {

             switch(select) {

                  case 1:       // 顯示目前停車

                       disp_cars();

                       break;

                  case 2:      // 設定現在時間

                       set_time();

                       break;

                  case 3:       // 車輛進入

                       enter_car();

                       break;

                  case 4:          // 車輛出場

                       outer_car();

                       break;

                  default:

                       System.out.printf("錯誤選擇, 請重新輸入\n");

             }

             select = get_menu();

         }

    }

 

    /* 主功能表選單 */

    static int get_menu()  {

        System.out.printf("\n** 高雄市前金立體停車場 管理系統 目前時間");

        System.out.printf(" %d %d **\n", Clock.hour, Clock.minute);

        System.out.printf("(1) 顯示目前停車      (2) 設定目前時間\n");

        System.out.printf("(3) 車輛進入取票      (4) 車輛出場繳費\n");

        System.out.printf("(5) 離開系統\n");

        System.out.printf("目前車輛 %d 請輸入工作選項 =>", Cars.number);

        int select = keyin.nextInt();

        keyin.nextLine();

        return select;

    }

 

    /* 顯示目前停車車輛 */

    static void disp_cars() {

        System.out.printf("停車票號\t\t進入時間\n");

        for(int i=0; i<Cars.number; i++) {

            System.out.printf("%d\t\t", Cars.car[i].getTicket());

            System.out.printf("%d %d \n", Cars.car[i].enterTime[0],

                           Cars.car[i].enterTime[1]);

        }

    }

 

    /* 設定目前公用時間 */

    static void set_time() {

        System.out.printf("請輸入目前時間(/) =>");

        String now = keyin.nextLine();

        Scanner s = new Scanner(now).useDelimiter("/");

        Clock.hour = s.nextInt();

        Clock.minute = s.nextInt();

    }

 

    /* 車輛進場 */

    static void enter_car() {

         Cars.car[Cars.number] = new carTicket();

         Cars.car[Cars.number].setTicket();

         Cars.car[Cars.number].enterTime[0] = Clock.hour;    //記錄時

         Cars.car[Cars.number].enterTime[1] = Clock.minute;    // 記錄分

         int ticket = Cars.car[Cars.number].getTicket();

         System.out.printf("請按<Enter>鍵取票 (票號:%d) =>", ticket);

         keyin.nextLine();

         Cars.number = Cars.number + 1;               // 下一輛號碼

     }

 

     /* 車輛出場, 繳費 */

     static void outer_car() {

         System.out.printf("請出示停車票 =>");

         int ticket = keyin.nextInt();

         keyin.nextLine();

         int flag =0, i;

         for(i=0; i<Cars.number; i++) {            // 搜尋該停車號

             if(ticket == Cars.car[i].getTicket()){

                 flag = 1;

                 break;

             }

         }

         if (flag == 0) {

            System.out.printf("查無此車輛 \n");

            return;

        }

        int hour = Clock.hour - Cars.car[i].enterTime[0];

        int minute = Clock.minute - Cars.car[i].enterTime[1];

        minute = minute + hour * 60;

        int tax = (minute/30) * 20;

        for(int j=i; j<Cars.number; j++)       // 刪除該車票紀錄

             Cars.car[j] = Cars.car[j+1];

        Cars.number = Cars.number - 1;         // 停車數 - 1

        System.out.printf("請繳交 %d 停車費(<Enter>) =>", tax);

        keyin.nextLine();

    }

}

 

7-5 專題製作儲蓄存款系統

『藝術銀行』期望製作一套『活期儲蓄存款系統』,程式設計師很難了解系統需求,因此依照可能狀態分段製作,每一階段完成並得到上級同意後,再往下一階段實現,吾人將各步驟分批實施如下。

7-5-1 範例研討:步驟(1) 建立存款帳戶規格

系統需要建立客戶帳戶。每一存款帳戶包含:姓名(String name)、帳戶(String No)與存款餘額(int balance),各項資料限制如下:

請製作一個帳戶類別(Account.class)是其具有上述功能,再至作主程式引用該類別,驗證是否正常。當建立帳戶時,只要輸入 12 個帳號,系統自動產生檢查碼;處理帳戶而輸入帳號,系統也會檢查檢查碼是否正確。

期望驗證帳戶類別功能的結果如下:

D:\Java2_book\chap7\PM7_2\PM7_2_1>javac Account.java     【編譯 Account.java

D:\Java2_book\chap7\PM7_2\PM7_2_1>javac PM7_2_1.java    【編譯 PM7_2_1.java

D:\Java2_book\chap7\PM7_2\PM7_2_1>dir/b

Account.class                                           【帳戶 Account 類別】

Account.java

PM7_2_1.class                                       【主程式 PM7_2_1 類別】

PM7_2_1.java

 

D:\Java2_book\chap7\PM7_2\PM7_2_1>java PM7_2_1         【執行主類別】

***建立新帳戶***                                       // 輸入帳戶驗證

請輸入姓名=>粘添壽

請輸入帳號(12 位數字) =>123451234589

****建立完成****

完整的新帳戶(13)=>12345123458910

請輸入存款金額 =>50000

餘額 = 50000

列印帳戶資料                                    // 輸出驗證結果

帳戶姓名: 粘添壽

帳戶號碼(13 )12345123458910

餘額= 50000

首先必須建立帳戶類別,其內容包含帳戶姓名(name)、帳號(ID)與餘額(balance),其中 ID  balance 為私有變數,必須透過物件方法存取,其架構如圖 7-11 所示。

7-16 Account.java 程式架構

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

//Account.java

/* 設定客戶姓名、帳號(ID) 與存款餘額 */

import java.io.*;

import java.util.Scanner;

 

class Account{

    String name;

    private int[]ID = new int[13];

    private int balance;

// 設定帳號

    int setID(String ID_Str) {

        if(ID_Str.length() !=12){

            System.out.printf("12個字元,請重新輸入!!\n");

            return 0;

        }

        else{

            Scanner s = new Scanner(ID_Str).useDelimiter("");

            int total = 0;

            for(int i=0;i<12;i++){

               ID[i]=s.nextInt();

               if((i+1)%2==0)

                   total+=ID[i];

               else

                   total+=ID[i]*2;

            }

            ID[12] = (10-(total%10));

            System.out.print("****建立完成****\n完整的新帳戶(13)=>");

            for(int i=0;i<13;i++)

                  System.out.print(ID[i]);

             System.out.println();

             return 1;          

       }

     }

 

// 取得帳號

    int[] getID(){

        return ID;

    }

// 寫入帳號

    int writeID(String ID_STR1) {

        if(ID_STR1.length() !=13){

            System.out.printf("長度不對\n");

            return 0;

        }

        else{

            Scanner s = new Scanner(ID_STR1).useDelimiter("");

            int total = 0, check, check_R;

            for(int i=0;i<12;i++){

               ID[i]=s.nextInt();

               if((i+1)%2==0)

                   total+=ID[i];

               else

                   total+=ID[i]*2;

            }

            check_R = s.nextInt();

            check = 10-(total%10);

            if (check == check_R) {

                ID[12] = check;

                return 1;

            } else {

                 System.out.printf("檢查碼不對\n");

                 return 0;

            }  

           }

    }    

 

// 存款

    int saveM(int money){

        balance = balance + money;

        return balance;

    }

// 取款

    int recM(int money) {

        int m = balance - money;

        if (m >=0) {

           balance = m;

           return balance;

        }

        else {

           return -1;

        }

    }

// 查詢餘額

    int checkM(){

        return balance;

    }   

}

我們寫一個簡單程式來驗證 Account 類別是否能滿足所需,程式範例如下:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

//PM7_2_1.java

 

import java.util.*;

public class PM7_2_1{

    public static void main(String[] args) {

        Scanner keyin = new Scanner(System.in);

        String ID_Str;

        Account customer = new Account();

        System.out.printf("***建立新帳戶***\n請輸入姓名=>");

        customer.name = keyin.nextLine();

        System.out.printf("請輸入帳號(12 位數字) =>");

        ID_Str = keyin.nextLine();

        int flag = customer.setID(ID_Str);

        if (flag == 0)

              return;

        System.out.printf("請輸入存款金額 =>");

        int money = keyin.nextInt();

        int balance = customer.saveM(money);

        System.out.printf("餘額 = %d\n", balance);

/* 列印帳戶清單 */

        System.out.printf("列印帳戶資料\n");

        System.out.printf("帳戶姓名: %s \n", customer.name);

        int[] ID = customer.getID();

        System.out.printf("帳戶號碼(13 )");

        for (int i=0; i<13; i++)

            System.out.printf("%d", ID[i]);

        System.out.printf("\n");

        System.out.printf("餘額= %d\n", customer.checkM());

    }

}